using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using Serilog;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Validation;
using Yyd.Share.Base;

namespace Yyd.Share.MiddleWares;

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private const string ErrorResponseType = "application/json";
    private const string CorsResponseHeader = "Access-Control-Allow-Origin";

    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context, IHostEnvironment env /* other dependencies */)
    {
        try
        {
            var requestId = context.TraceIdentifier??Guid.NewGuid().ToString("N");
            context.Response.Headers.TryAdd("X-CRM-TraceId",requestId);
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex, env);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception, IHostEnvironment env)
    {
        var wrapData = WrapData(context, exception, env);
        context.Response.StatusCode = StatusCodes.Status200OK;
        context.Response.ContentType = ErrorResponseType;
        
        var setting = new JsonSerializerSettings
        {
            ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
        };
        var requestId = context.TraceIdentifier??Guid.NewGuid().ToString("N");
        // context.Response.Headers.TryAdd("X-CRM-TraceId",requestId);
        
        var res = JsonConvert.SerializeObject(wrapData, Formatting.None, setting);       

        return context.Response.WriteAsync(res);
    }

    public static BaseResultModel WrapData(HttpContext context, Exception ex, IHostEnvironment env)
    {
        var logTemplate = "{AppName}";
        
        BaseResultModel baseResultModel;

        var isValidationException = ex is AbpValidationException;

        if (isValidationException)
        {
            var validationException = (AbpValidationException)ex;
            logTemplate = $"{logTemplate} {nameof(AbpValidationException)}:";
            baseResultModel = BaseResultModel
                .Failed(ReturnCode.ParamError, "请求参数错误！", validationException.ValidationErrors);
        }
        else if (ex is DataNotExistsException dataNotExistsException)
        {
            logTemplate = $"{logTemplate} {nameof(DataNotExistsException)}:";
            baseResultModel = BaseResultModel.Failed(dataNotExistsException.ErrorCode, dataNotExistsException.Message);
        }
        else if (ex is OperationException _)
        {
            logTemplate = $"{logTemplate} {nameof(OperationException)}:";
            baseResultModel = BaseResultModel.Failed(ReturnCode.OperationError, ex.Message);
        }
        else if (ex is UserFriendlyException _)
        {
            logTemplate = $"{logTemplate} {nameof(UserFriendlyException)}:";
            baseResultModel = BaseResultModel.Failed(ReturnCode.OperationError, ex.Message);
        }
        //else if (ex is HttpProxyException)
        //{
        //    //外部服务接口请求异常
        //    logTemplate = $"{logTemplate} {nameof(HttpProxyException)}:";
        //    baseResultModel = BaseResultModel.Failed(ReturnCode.OperationError, ex.Message);
        //}
        else
        {
            logTemplate = $"{logTemplate} RuntimeException:";

            if (env != null && string.Equals(env.EnvironmentName, "live", StringComparison.OrdinalIgnoreCase))
            {
                baseResultModel = BaseResultModel.Failed(ReturnCode.Failure, "偶然性网络不稳定，请重试。");
            }
            else
            {
                baseResultModel = BaseResultModel.Failed(ReturnCode.Failure, $"处理失败 {ex.StackTrace}----{ex.Message}");
            }
        }

        if (isValidationException)
        {
            Log.ForContext<ErrorHandlingMiddleware>().Error(logTemplate);
        }
        else
        {
            Log.ForContext<ErrorHandlingMiddleware>().Error(ex, logTemplate);
        }

        return baseResultModel;
    }
}